﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Diagnostics;
using System.Windows.Threading;

using SwyishChess.Domain.Board;
using SwyishChess.Domain.ChessGame;
using SwyishChess.Domain.Move;
using SwyishChess.Domain.Piece;
using SwyishChess.Domain.Player;
using SwyishChess.Service;

namespace SwyishChess.DesktopUI
{
    public partial class MainWindow : Window
    {
        // Image uri constants
        private const string BLACK_PAWN_IMAGE = "./Images/blackPawn.png";
        private const string BLACK_BISHOP_IMAGE = "./Images/blackBishop.png";
        private const string BLACK_KNIGHT_IMAGE = "./Images/blackKnight.png";
        private const string BLACK_ROOK_IMAGE = "./Images/blackRook.png";
        private const string BLACK_QUEEN_IMAGE = "./Images/blackQueen.png";
        private const string BLACK_KING_IMAGE = "./Images/blackKing.png";
        private const string WHITE_PAWN_IMAGE = "./Images/whitePawn.png";
        private const string WHITE_BISHOP_IMAGE = "./Images/whiteBishop.png";
        private const string WHITE_KNIGHT_IMAGE = "./Images/whiteKnight.png";
        private const string WHITE_ROOK_IMAGE = "./Images/whiteRook.png";
        private const string WHITE_QUEEN_IMAGE = "./Images/whiteQueen.png";
        private const string WHITE_KING_IMAGE = "./Images/whiteKing.png";

        // Chess board details
        private const int CHESS_BLOCK_WIDTH = 80;
        private const int CHESS_BLOCK_HEIGHT = 80;
        private const int CHESS_BOARD_ROW = 8;
        private const int CHESS_BOARD_COLUMN = 8;

        // Player details
        private const int PLAYER_ONE = 0;
        private const int PLAYER_TWO = 1;
        private const int WHITE_PLAYER = 1;
        private const int BLACK_PLAYER = 2;

        /// <summary>
        /// Prepresents the chess game
        /// </summary>
        public ChessGameModel ChessGameModel { get; set; }

        /// <summary>
        /// To keep track of the currently selected chess piece
        /// </summary>
        public ChessPiece SelectedChessPiece { get; set; }

        /// <summary>
        /// Used to calculate the time elapsed
        /// </summary>
        public Stopwatch StopWatch { get; set; }
        /// <summary>
        /// Used to calculate the time elapsed
        /// </summary>
        public DispatcherTimer Timer { get; set; }

        // Chess piece movement validation service
        private IMoveValidationService _moveValidationService;
        // Chess piece movement tracking service
        private IMoveTrakingService _moveTrackingService;
        // Special chess piece movement service
        private ISpecialMoveService _specialMoveService;

        /// <summary>
        /// Overloaded constructor which takes the chess game model, and dependencies such as
        /// move validation service, move tracking service, and special move service
        /// </summary>
        /// <param name="chessGameModel">Chess Game object which contains all chess game components</param>
        /// <param name="moveValidationService">Dependency that validates moves</param>
        /// <param name="moveTrackingSerice">Dependency that tracks moves</param>
        /// <param name="specialMoveService">Dependency that dose special moves</param>
        public MainWindow(ChessGameModel chessGameModel, IMoveValidationService moveValidationService,
            IMoveTrakingService moveTrackingSerice,ISpecialMoveService specialMoveService)
        {
            InitializeComponent();

            ChessGameModel = chessGameModel;
            _moveValidationService = moveValidationService;
            _moveTrackingService = moveTrackingSerice;
            _specialMoveService = specialMoveService;

            /* PUTS THE WINDOW IN THE CENTER OF THE SCREEN */
            WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;

            StartTimers();
            DrawAll();
        }

        /// <summary>
        /// Creates and start the timer
        /// </summary>
        private void StartTimers()
        {
            Timer = new DispatcherTimer();
            StopWatch = new Stopwatch();

            // To update time each second
            Timer.Interval = TimeSpan.FromSeconds(1);
            Timer.Tick += Timer_Tick;

            Timer.Start();
            StopWatch.Start();
        }

        /// <summary>
        /// Timer tick event which keeps updating the time elapsed text block
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Timer_Tick(object sender, EventArgs e)
        {
            TimeSpan ts = StopWatch.Elapsed;
            textBlockTimeElapsed.Content = "Time Elapsed: " + ts.ToString("hh':'mm':'ss");
        }

        /// <summary>
        /// Deletes the entire game board and re-draws.
        /// </summary>
        private void DrawAll()
        {
            GameBoardCanvas.Children.Clear();
            DrawGameBoard();
            DisplayCapturedPieces();

            CheckForCheck();
            CheckForPawnPromotion();

            // Make the list box to show the most recently added item
            if (MoveListBox.Items.Count > 1)
            {
                MoveListBox.ScrollIntoView(MoveListBox.Items[MoveListBox.Items.Count - 1]);
            }

            CheckForPossibleMovesLeft();
            int whiteCount, blackCount;
            AddAllPossibleMovesForPlayer(out blackCount, out whiteCount);

            CheckForGameOver(whiteCount, blackCount);
        }

        /// <summary>
        /// Displays the captured pieces on the dead pieces stack panel
        /// </summary>
        private void DisplayCapturedPieces()
        {
            BlackPiecesCanvas.Children.Clear();
            WhitePiecesCanvas.Children.Clear();

            for (int index = 0; index < ChessGameModel.ChessMoves.MoveTracker.Count; index++)
            {
                if (ChessGameModel.ChessMoves.MoveTracker[index].CapturedPiece != null)
                {
                    BitmapImage image = null;
                    ChessPiece cp = ChessGameModel.ChessMoves.MoveTracker[index].CapturedPiece;

                    // if the peice belongs to the black player
                    if (cp.OwningPlayerId == BLACK_PLAYER)
                    {
                        image = GetBlackPieceImage(image, cp);

                        Image img = new Image();
                        img.Source = image;
                        // display the captured piece on the white players canvas
                        WhitePiecesCanvas.Children.Add(img);
                    }
                    else
                    {
                        image = GetWhitePieceImage(image, cp);

                        Image img = new Image();
                        img.Source = image;
                        BlackPiecesCanvas.Children.Add(img);
                    }
                }
            }
        }

        /// <summary>
        /// Get white piece images
        /// </summary>
        /// <param name="image"></param>
        /// <param name="cp">Chess piece to get the image</param>
        /// <returns>Chess piece image</returns>
        private BitmapImage GetWhitePieceImage(BitmapImage image, ChessPiece cp)
        {
            if (cp is Pawn) image = new BitmapImage(new Uri(WHITE_PAWN_IMAGE, UriKind.Relative));
            else if (cp is Bishop) image = new BitmapImage(new Uri(WHITE_BISHOP_IMAGE, UriKind.Relative));
            else if (cp is Knight) image = new BitmapImage(new Uri(WHITE_KNIGHT_IMAGE, UriKind.Relative));
            else if (cp is Rook) image = new BitmapImage(new Uri(WHITE_ROOK_IMAGE, UriKind.Relative));
            else if (cp is Queen) image = new BitmapImage(new Uri(WHITE_QUEEN_IMAGE, UriKind.Relative));
            if (cp is King) image = new BitmapImage(new Uri(WHITE_KING_IMAGE, UriKind.Relative));
            return image;
        }

        /// <summary>
        /// Get black piece images
        /// </summary>
        /// <param name="image"></param>
        /// <param name="cp">Chess piece to get the image</param>
        /// <returns>Chess piece image</returns>
        private BitmapImage GetBlackPieceImage(BitmapImage image, ChessPiece cp)
        {
            if (cp is Pawn) image = new BitmapImage(new Uri(BLACK_PAWN_IMAGE, UriKind.Relative));
            else if (cp is Bishop) image = new BitmapImage(new Uri(BLACK_BISHOP_IMAGE, UriKind.Relative));
            else if (cp is Knight) image = new BitmapImage(new Uri(BLACK_KNIGHT_IMAGE, UriKind.Relative));
            else if (cp is Rook) image = new BitmapImage(new Uri(BLACK_ROOK_IMAGE, UriKind.Relative));
            else if (cp is Queen) image = new BitmapImage(new Uri(BLACK_QUEEN_IMAGE, UriKind.Relative));
            else if (cp is King) image = new BitmapImage(new Uri(BLACK_KING_IMAGE, UriKind.Relative));
            return image;
        }

        /// <summary>
        /// Checks how many movements are left for the player
        /// </summary>
        private void CheckForPossibleMovesLeft()
        {
            // TODO: This method should be moved to move validation or chess piece model
            for (int row = 0; row < CHESS_BOARD_ROW; row++)
            {
                for (int col = 0; col < CHESS_BOARD_COLUMN; col++)
                {
                    if (ChessGameModel.ChessPieces.ChessPieces[row, col] != null)
                    {
                        _moveValidationService.FindMovableChessBlock(ChessGameModel.ChessPieces.ChessPieces, 
                            ChessGameModel.ChessBoard.ChessBoard, 
                            ChessGameModel.ChessPieces.ChessPieces[row, col], row, col);
                        _moveValidationService.RemoveCheckCausingMoves(ChessGameModel.ChessPieces, ChessGameModel.ChessBoard, ChessGameModel.ChessPlayers.PlayerTurn.PlayerId,
                             row, col, ChessGameModel.ChessBoard.ChessBoard, ChessGameModel.ChessPieces.ChessPieces[row, col]);
                        ChessGameModel.ChessBoard.RemoveMovableRange();
                    }
                }
            }
        }

        /// <summary>
        /// Add all possible movement counts for each pieces in the game board
        /// </summary>
        /// <param name="blackCount">Black player's possible movement counts</param>
        /// <param name="whiteCount">White player's possible movement counts</param>
        private void AddAllPossibleMovesForPlayer(out int blackCount, out int whiteCount)
        {
            // TODO: This method should be moved to move validation or chess piece model
            blackCount = 0;
            whiteCount = 0;

            for (int row = 0; row < 8; row++)
            {
                for (int col = 0; col < 8; col++)
                {
                    if (ChessGameModel.ChessPieces.ChessPieces[row, col] != null)
                    {
                        if (ChessGameModel.ChessPieces.ChessPieces[row, col].OwningPlayerId == 2)
                        {
                            blackCount += ChessGameModel.ChessPieces.ChessPieces[row, col].PossibleMoveCount;
                        }
                        else
                        {
                            whiteCount += ChessGameModel.ChessPieces.ChessPieces[row, col].PossibleMoveCount;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Creates the visual represetation of the game board and chess pieces
        /// </summary>
        private void DrawGameBoard()
        {
            for (int row = 0; row < 8; row++)
            {
                for (int column = 0; column < 8; column++)
                {
                    // Add all chess blocks to the boards
                    GameBoardCanvas.Children.Add(InitializeGameGrid(row, column));

                    // Only add a chess piece if there is one in the ChsesPieces array
                    if (ChessGameModel.ChessPieces.ChessPieces[row, column] != null)
                    {
                        GameBoardCanvas.Children.Add(InitializeGamePieces(row, column));
                    }
                }
            }
        }

        /// <summary>
        /// Decides if a player won the game
        /// </summary>
        private void CheckForGameOver(int whiteCount, int blackCount)
        {
            if (_moveValidationService.CheckForGameOver(ChessGameModel.ChessPieces.ChessPieces) == true)
            {
                // Checks who owns the last move which captured a king
                if (ChessGameModel.ChessMoves.MoveTracker[ChessGameModel.ChessMoves.MoveTracker.Count - 1]
                    .ChessPiece.OwningPlayerId == WHITE_PLAYER)
                {
                    textBlockTurn.Text = "WHITE WON!";
                }
                else
                {
                    textBlockTurn.Text = "BLACK WON!";
                }

                // Remove all click events from the game board
                for (int index = 0; index < GameBoardCanvas.Children.Count - 1; index++)
                {
                    GameBoardCanvas.Children[index].MouseLeftButtonDown -= GamePieceButton_Click;
                }      
            }

            //TODO: Refactor
            if (whiteCount == 0)
            {
                if (ChessGameModel.ChessPlayers.ChessPlayers[WHITE_PLAYER].IsChecked == false
                    && ChessGameModel.ChessPlayers.ChessPlayers[BLACK_PLAYER].IsChecked == false)
                {
                    textBlockWarning.Text = "StaleMate!";
                }
                else
                {
                    textBlockWarning.Text = "CheckMate!";
                    textBlockTurn.Text = "Black Won!";
                    MoveListBox.Items.Add(textBlockWarning.Text + " " + textBlockTurn.Text);
                }
            }

            if (blackCount == 0)
            {
                if (ChessGameModel.ChessPlayers.ChessPlayers[PLAYER_ONE].IsChecked == false
                    && ChessGameModel.ChessPlayers.ChessPlayers[PLAYER_TWO].IsChecked == false)
                {
                    textBlockWarning.Text = "StaleMate!";
                }
                else
                {
                    textBlockWarning.Text = "CheckMate!";
                    textBlockTurn.Text = "White Won!";
                    MoveListBox.Items.Add(textBlockWarning.Text + " " + textBlockTurn.Text);
                }
            }
        }

        /// <summary>
        /// Sets various properties of a chess block used to create a game board
        /// </summary>
        /// <param name="row">Row location of the block in chess game</param>
        /// <param name="column">Colum location of the block in chess game</param>
        /// <returns>Returns a rectangle block that represents a chess block</returns>
        private Rectangle InitializeGameGrid(int row, int column)
        {
            Rectangle newRectangle = new Rectangle();

            newRectangle.Name = "y" + row.ToString() + "x" + column.ToString();

            newRectangle.RadiusX = 0;
            newRectangle.RadiusY = 0;

            newRectangle.Width = CHESS_BLOCK_WIDTH;
            newRectangle.Height = CHESS_BLOCK_HEIGHT;

            newRectangle.Margin = new Thickness(column * newRectangle.Width, row * newRectangle.Height, newRectangle.Margin.Right, newRectangle.Margin.Bottom);

            newRectangle.StrokeThickness = 2;
            newRectangle.Stroke = Brushes.Black;

            if (ChessGameModel.ChessBoard.ChessBoard[row, column].Color == "on")
                newRectangle.Fill = Brushes.Gray;
            else
                newRectangle.Fill = Brushes.LightGray;

            // If the Chess board that contains the chess piece is marked available for move
            if (ChessGameModel.ChessBoard.ChessBoard[row, column].IsAvailableForMove == true)
            {
                // Add a click event
                newRectangle.Fill = Brushes.Coral;
                newRectangle.MouseLeftButtonDown += OnRectangleClick;
            }

            return newRectangle;
        }

        /// <summary>
        /// Creates visual representation of each ches pieces
        /// </summary>
        /// <param name="row">Row location of the chess piece</param>
        /// <param name="column">Column location of the chess piece</param>
        /// <returns>Rectangle representation of chses piece</returns>
        private Rectangle InitializeGamePieces(int row, int column)
        {
            Rectangle chessPiece = new Rectangle();
            ChessPiece cp = ChessGameModel.ChessPieces.ChessPieces[row, column];

            // Creating a name for the chess piece rectangle in a format of "Y0X0"
            chessPiece.Name = "Y" + row.ToString() + "X" + column.ToString();

            /* Add left mouse button click event to each chess pieces
             * if it is the chess piece's color's turn. */
            if (cp.OwningPlayerId == ChessGameModel.ChessPlayers.PlayerTurn.PlayerId)
            {
                chessPiece.MouseLeftButtonDown += GamePieceButton_Click;
            }

            ImageBrush brush1 = new ImageBrush();
            BitmapImage image = null;

            if (cp.OwningPlayerId == ChessGameModel.ChessPlayers.ChessPlayers[1].PlayerId)
            {
                image = GetBlackPieceImage(image, cp);
            }
            else
            {
                image = GetWhitePieceImage(image, cp);
            }

            brush1.ImageSource = image;
            chessPiece.Fill = brush1;

            // Setting the size and margin of the chess piece rectangle so the background can be clickable
            chessPiece.Width = 50;
            chessPiece.Height = 50;
            chessPiece.Margin = new Thickness(column * CHESS_BLOCK_WIDTH + 15, row * CHESS_BLOCK_HEIGHT + 15,
                chessPiece.Margin.Right, chessPiece.Margin.Bottom);

            return chessPiece;
        }

        /// <summary>
        /// Click events used when a chess piece is clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void GamePieceButton_Click(object sender, RoutedEventArgs e)
        {// Y0X0
            int row = Int32.Parse(((Rectangle)sender).Name[1].ToString());
            int column = Int32.Parse(((Rectangle)sender).Name[3].ToString());

            ChessGameModel.ChessBoard.RemoveMovableRange();

            SelectedChessPiece = ChessGameModel.ChessPieces.ChessPieces[row, column];

            // Mark all movable blocks
            _moveValidationService.FindMovableChessBlock(ChessGameModel.ChessPieces.ChessPieces, ChessGameModel.ChessBoard.ChessBoard, SelectedChessPiece, row, column);
            _moveValidationService.RemoveCheckCausingMoves(ChessGameModel.ChessPieces, ChessGameModel.ChessBoard, 
                ChessGameModel.ChessPlayers.PlayerTurn.PlayerId, row, column, ChessGameModel.ChessBoard.ChessBoard, SelectedChessPiece);

            DrawAll();
        }

        /// <summary>
        /// Click event when a movable chess block is clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnRectangleClick(object sender, MouseButtonEventArgs e)
        {
            // Gets the row and column of the clicked rectangle
            int row = Int32.Parse(((Rectangle)sender).Name[1].ToString());
            int column = Int32.Parse(((Rectangle)sender).Name[3].ToString());

            // Gets the previous location of the currently selected for moving chess piece
            int previousRow, previousColumn;
            ChessGameModel.ChessPieces.GetLocationOfChessPiece(SelectedChessPiece, out previousRow, out previousColumn);

            // Gets the reference to the captured chess piece
            ChessPiece capturedPiece = ChessGameModel.ChessPieces.ChessPieces[row, column];

            // Remove the chess piece from the current location
            ChessGameModel.ChessPieces.RemoveChessPiece(SelectedChessPiece);
            // and set it to the new location
            ChessGameModel.ChessPieces.SetNewChessPieceLocation(row, column, SelectedChessPiece);

            // Change the turn
            ChangeTurn();

            ChessGameModel.ChessBoard.RemoveMovableRange();

            // Handles special moves and records them
            HandleSpecialMoves(row, column, previousRow, previousColumn, capturedPiece);

            if (!(SelectedChessPiece is Pawn) && !(SelectedChessPiece is King) && !(SelectedChessPiece is Rook))
            {
                // Record the movement
                RecordMove(SelectedChessPiece, previousRow, previousColumn, row, column, capturedPiece); 
            }

            DrawAll();
        }

        /// <summary>
        /// Handles special moves
        /// </summary>
        /// <param name="row"></param>
        /// <param name="column"></param>
        /// <param name="previousRow"></param>
        /// <param name="previousColumn"></param>
        /// <param name="capturedPiece"></param>
        private void HandleSpecialMoves(int row, int column, int previousRow, int previousColumn, ChessPiece capturedPiece)
        {
            // Checks if the current move activates a special move
            // If the chess piece was king
            if (SelectedChessPiece is King)
            {
                // and if the king moved 2 columns either to left or right, do castling
                if (column - previousColumn == 2 || previousColumn - column == 2)
                {
                    ChessPiece affectedPiece;
                    _specialMoveService.DoCastling(ChessGameModel.ChessPieces.ChessPieces, SelectedChessPiece,
                        previousColumn, column, out affectedPiece);
                    RecordSpecialMove(SelectedChessPiece, previousRow, previousColumn, row, column, capturedPiece, SpecialMove.Castling);
                    _moveTrackingService.AddAffectedPieceToListMove(ChessGameModel.ChessMoves.MoveTracker, affectedPiece);

                    CheckAlertWindow checkWindow = new CheckAlertWindow("Castling!");
                    checkWindow.ShowDialog();
                }
                else
                {
                    // Record the movement
                    RecordMove(SelectedChessPiece, previousRow, previousColumn, row, column, capturedPiece);
                }
            }
            // if the chess piece was rook
            else if (SelectedChessPiece is Rook)
            {
                // Record the movement
                RecordMove(SelectedChessPiece, previousRow, previousColumn, row, column, capturedPiece);
            }
            // if the chess piece that moved was a pawn
            else if (SelectedChessPiece is Pawn)
            {
                // If the pawn moved 2 rows, set it it is eligible to be captured by en passant
                if (previousRow - row == 2 || row - previousRow == 2)
                {
                    ((Pawn)SelectedChessPiece).WasLastMove = true;
                }
                else
                {
                    _moveValidationService.DisableEligibilityForEnPassant(ChessGameModel.ChessPieces.ChessPieces);
                }

                if (_moveValidationService.CheckIfLastMoveWasEnPassant(ChessGameModel.ChessPieces.ChessPieces, SelectedChessPiece))
                {
                    ChessPiece capturedByEnPassant;
                    _specialMoveService.DoEnPassant(ChessGameModel.ChessPieces.ChessPieces, SelectedChessPiece, out capturedByEnPassant);
                    RecordSpecialMove(SelectedChessPiece, previousRow, previousColumn, row, column, capturedPiece, SpecialMove.EnPassant);
                    _moveTrackingService.AddCapturedPieceToListMove(ChessGameModel.ChessMoves.MoveTracker, capturedByEnPassant);

                    CheckAlertWindow newWindow = new CheckAlertWindow("En Passant!");
                    newWindow.Show();
                }
                else
                {
                    // Record the movement
                    RecordMove(SelectedChessPiece, previousRow, previousColumn, row, column, capturedPiece);
                }

                // If currently selected chess piece is not pawn, disable eligibility for en passant
                if (!(SelectedChessPiece is Pawn))
                {
                    _moveValidationService.DisableEligibilityForEnPassant(ChessGameModel.ChessPieces.ChessPieces);
                }
            }
        }

        /// <summary>
        /// Checks if the current move caused a check
        /// </summary>
        private void CheckForCheck()
        {
            int checkedPlayer;

            if (_moveValidationService.CheckForCheck(ChessGameModel.ChessPieces.ChessPieces, ChessGameModel.ChessBoard.ChessBoard, out checkedPlayer) == true)
            {
                // black player is player 2 and white player is player 1
                if (checkedPlayer == BLACK_PLAYER)
                {
                    ChessGameModel.ChessPlayers.ChessPlayers[PLAYER_ONE].IsChecked = true;
                    ChessGameModel.ChessPlayers.ChessPlayers[PLAYER_TWO].IsChecked = false;
                }
                else if (checkedPlayer == WHITE_PLAYER)
                {
                    ChessGameModel.ChessPlayers.ChessPlayers[PLAYER_TWO].IsChecked = true;
                    ChessGameModel.ChessPlayers.ChessPlayers[PLAYER_ONE].IsChecked = false;
                }
                else
                {
                    ChessGameModel.ChessPlayers.ChessPlayers[PLAYER_TWO].IsChecked = false;
                    ChessGameModel.ChessPlayers.ChessPlayers[PLAYER_ONE].IsChecked = false;
                }

                // Reverts the move if the move causes a check and the move was from the same player who owns the king that was checked
                if (ChessGameModel.ChessMoves.MoveTracker[ChessGameModel.ChessMoves.MoveTracker.Count - 1].ChessPiece.OwningPlayerId == checkedPlayer)
                {
                    _moveTrackingService.UndoMove(ChessGameModel.ChessMoves.MoveTracker, ChessGameModel.ChessMoves.UndoedMoveTracker,
                        ChessGameModel.ChessPieces.ChessPieces, ChessGameModel.ChessMoves.MoveTracker.Count - 1);
                    MoveListBox.Items.RemoveAt(MoveListBox.Items.Count - 1);

                    ChangeTurn();
                    ChessGameModel.ChessBoard.RemoveMovableRange();
                    DrawAll();
                }
                else
                {
                    // Only If the check has not been declared right before this move
                    if (textBlockWarning.Text == "" )
                    {
                        // Creates a alert window
                        CheckAlertWindow checkWindow = new CheckAlertWindow();
                        checkWindow.ShowDialog();
                    }

                    textBlockWarning.Text = "CHECK!";
                    ChessGameModel.ChessBoard.RemoveMovableRange();
                }

            }
            else
            {
                textBlockWarning.Text = "";
                ChessGameModel.ChessBoard.RemoveMovableRange();
            }
        }

        /// <summary>
        /// Shows the changed turn on the screen and also change the turn in the ChessGameModel
        /// </summary>
        private void ChangeTurn()
        {
            if (ChessGameModel.ChessPlayers.PlayerTurn == ChessGameModel.ChessPlayers.ChessPlayers[PLAYER_ONE])
            {
                textBlockTurn.Text = "Black's Turn";
            }
            if (ChessGameModel.ChessPlayers.PlayerTurn == ChessGameModel.ChessPlayers.ChessPlayers[PLAYER_TWO])
            {
                textBlockTurn.Text = "White's Turn";
            }

            ChessGameModel.ChessPlayers.ChangePlayerTurn();
        }

        /// <summary>
        /// Records regular chess moves
        /// </summary>
        /// <param name="cp">Moved chess piece</param>
        /// <param name="currentRow">Current row location</param>
        /// <param name="currentColumn">Current column location</param>
        /// <param name="newRow">New row location</param>
        /// <param name="newColumn">New column location</param>
        /// <param name="capturedPiece">Captured chess piece</param>
        private void RecordMove(ChessPiece cp, int currentRow, int currentColumn, int newRow, int newColumn, ChessPiece capturedPiece)
        {
            _moveTrackingService.RecordMove(ChessGameModel.ChessMoves.MoveTracker, cp, currentRow, currentColumn, newRow, newColumn, capturedPiece);

            MoveListBox.Items.Add(GetTimeElapsed() + " " + ChessGameModel.ChessMoves.MoveTracker[ChessGameModel.ChessMoves.MoveTracker.Count - 1].ToString());
        }

        /// <summary>
        /// Recoards special move made by players
        /// </summary>
        /// <param name="cp">Moved chess piece</param>
        /// <param name="currentRow">Current row location</param>
        /// <param name="currentColumn">Current column location</param>
        /// <param name="newRow">New row location</param>
        /// <param name="newColumn">New column location</param>
        /// <param name="capturedPiece">Captured chess piece</param>
        /// <param name="specialMove">Special move type</param>
        private void RecordSpecialMove(ChessPiece cp, int currentRow, int currentColumn, int newRow, int newColumn,
            ChessPiece capturedPiece, SpecialMove specialMove)
        {
            _moveTrackingService.RecordMove(ChessGameModel.ChessMoves.MoveTracker, cp, currentRow, currentColumn,
                newRow, newColumn, capturedPiece, specialMove);

            MoveListBox.Items.Add(GetTimeElapsed() + " " + ChessGameModel.ChessMoves.MoveTracker[ChessGameModel.ChessMoves.MoveTracker.Count - 1].ToString());
        }

        /// <summary>
        /// Get the elapsed time in string format
        /// </summary>
        /// <returns>The elapsed time in a string format</returns>
        private String GetTimeElapsed()
        {
            TimeSpan ts = StopWatch.Elapsed;
            return ts.ToString("hh':'mm':'ss");
        }

        /// <summary>
        /// When window was closed by the user
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnWindowClosed(object sender, EventArgs e)
        {
            Application.Current.Shutdown(0);
        }

        /// <summary>
        /// Click vent for the quit button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ButtonQuit_Click(object sender, RoutedEventArgs e)
        {
            Application.Current.Shutdown(0);
        }

        /// <summary>
        /// Click event for the restart button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ButtonRestart_Click(object sender, RoutedEventArgs e)
        {
            // Starts new application
            System.Diagnostics.Process.Start(Application.ResourceAssembly.Location);
            // Shuts down the current application
            Application.Current.Shutdown();
        }

        /// <summary>
        /// Click event when undo button is clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ButtonUndo_Click(object sender, RoutedEventArgs e)
        {
            if (ChessGameModel.ChessMoves.MoveTracker.Count > 0)
            {
                // Reverts the turn
                ChangeTurn();

                // Record the undo on the list box
                MoveListBox.Items.Add(GetTimeElapsed() + " UNDO " + ChessGameModel.ChessMoves.MoveTracker[ChessGameModel.ChessMoves.MoveTracker.Count - 1].ToString());

                // Undo the last move
                _moveTrackingService.UndoMove(ChessGameModel.ChessMoves.MoveTracker, ChessGameModel.ChessMoves.UndoedMoveTracker,
                    ChessGameModel.ChessPieces.ChessPieces, ChessGameModel.ChessMoves.MoveTracker.Count - 1);

                DrawAll();
            }
        }

        /// <summary>
        /// Click event for a redo button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ButtonRedo_Click(object sender, RoutedEventArgs e)
        {
            if (ChessGameModel.ChessMoves.UndoedMoveTracker.Count > 0)
            {
                ChangeTurn();

                // Record the redo in list box
                MoveListBox.Items.Add(GetTimeElapsed() + " REDO " + ChessGameModel.ChessMoves.UndoedMoveTracker[ChessGameModel.ChessMoves.UndoedMoveTracker.Count - 1].ToString());

                // Redo the move
                _moveTrackingService.RedoMove(ChessGameModel.ChessMoves.MoveTracker, ChessGameModel.ChessMoves.UndoedMoveTracker,
                    ChessGameModel.ChessPieces.ChessPieces, ChessGameModel.ChessMoves.UndoedMoveTracker.Count - 1);

                DrawAll();
            }
        }

        /// <summary>
        /// Checks and promprts the player for promotion
        /// </summary>
        private void CheckForPawnPromotion()
        {
            if (_moveValidationService.CheckForPawnPromotion(ChessGameModel.ChessPieces.ChessPieces, SelectedChessPiece) == true)
            {
                ChessPiece affectedPiece = null;

                // Shows the promotino window
                PawnPromotionWindow promotionWindow = new PawnPromotionWindow();
                // If okay button is clicked
                if (promotionWindow.ShowDialog() == true)
                {
                    // Do promotion based on the selected string from the promotion window
                    string result = promotionWindow.ComboBoxUpgradables.SelectedItem.ToString();
                    _specialMoveService.DoPawnPromotion(ChessGameModel.ChessPieces.ChessPieces, result,
                        SelectedChessPiece, SelectedChessPiece.OwningPlayerId, out affectedPiece);

                    MoveListBox.Items.Add("Pawn was promoted!");

                    DrawAll();
                }
            }
        }
    }
}
